Skip to content

Conversation

@paulf81
Copy link
Collaborator

@paulf81 paulf81 commented Dec 9, 2025

Some of the lines in get_measurements unnecessarily slow the function down. This small PR gains speed up by:

  1. Saving the names of lmp_da_keys rather then running string formatting and list comprehension each step
  2. Caching h_dict["external_signals"] into external_signals

Profiling suggests this is worth a 2.5x speedup, going to run a full case to confirm

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the get_measurements method in HerculesInterface for improved performance. The optimization focuses on eliminating repeated string formatting operations and reducing dictionary lookup overhead, achieving an approximately 2.5x speedup according to profiling results.

Key changes:

  • Pre-computes LMP day-ahead keys once in __init__ instead of formatting them on every call
  • Caches h_dict["external_signals"] in a local variable to avoid repeated dictionary lookups
  • Refactors the forecast loop to use .items() instead of .keys() for more efficient iteration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@paulf81
Copy link
Collaborator Author

paulf81 commented Dec 9, 2025

Full case run is 10% faster, so less impact when all-in, but probably still worth it

Copy link
Collaborator

@misi9170 misi9170 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced that all of the changes are needed here, in particular, the separate declaration of the temporary external_signals variable. Even the main change, setting a new attribute _lmp_da_keys, is a bit borderline because it seems to trade readability (["lmp_da_{:02d}".format(h)] for h in range(24)) for something slightly more obscure (k in self._lmp_da_keys). At 10% improvement in end-to-end performance (thanks for reporting this, @paulf81 !), I think this is worth it, but just pointing out here that this is about the limit in my mind.


# Handle external signals (parse and pass to individual components)
if "external_signals" in h_dict:
external_signals = h_dict["external_signals"]
Copy link
Collaborator

@misi9170 misi9170 Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change necessary? In my mind, this obfuscates the code---it makes it less clear that the external_signals are coming from the passed-in h_dict. It's also meaning quite a few changes to the code, so unless it actually improves performance meaningfully, I'd rather not take it because it puts a seemingly unnecessary change in the version history for these lines

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, ran some tests and almost all the benefit is from LMP key pre-allocation. Will revert the other changes

Comment on lines 165 to 167
for k, v in external_signals.items():
if "forecast" in k:
measurements["forecast"][k] = h_dict["external_signals"][k]
measurements["forecast"][k] = v
Copy link
Collaborator

@misi9170 misi9170 Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I think the suggested form probably is a little more pythonic, so I'm ok with this change (possibly after reverting to h_dict["external_signals"].items())---but it's still a bit doubtful to me that there is a real performance enhancement

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

@misi9170
Copy link
Collaborator

misi9170 commented Dec 10, 2025

Pull request overview

This PR optimizes the get_measurements method in HerculesInterface for improved performance. The optimization focuses on eliminating repeated string formatting operations and reducing dictionary lookup overhead, achieving an approximately 2.5x speedup according to profiling results.

Key changes:

* Pre-computes LMP day-ahead keys once in `__init__` instead of formatting them on every call

* Caches `h_dict["external_signals"]` in a local variable to avoid repeated dictionary lookups

* Refactors the forecast loop to use `.items()` instead of `.keys()` for more efficient iteration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

How much of the 2.5x speedup comes from each of these changes? (does Copilot respond to questions in this format? Seems not, unfortunately)

@paulf81
Copy link
Collaborator Author

paulf81 commented Dec 10, 2025

Thanks for your comments @misi9170 , the changes should now be limited to those most worthwhile

@paulf81 paulf81 force-pushed the feature/preallocate_measurements branch from 7e4e721 to 7910e5a Compare December 10, 2025 17:12
@misi9170 misi9170 added the enhancement New feature or request label Dec 10, 2025
Copy link
Collaborator

@misi9170 misi9170 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @paulf81 !

@misi9170
Copy link
Collaborator

Merged develop back to make sure all in line with new ruff formatting rules---seems to be the case, so I'll go ahead and merge once checks pass.

@misi9170 misi9170 merged commit b7a0c91 into NREL:develop Dec 10, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants